package com.andkrup.geom {
	import flash.geom.Point;
	
	/**
	 * @author Andkrup
	 */
	public class Vector2D {
		public var x:Number;
		public var y:Number;
		public var direction:Number;
		public var range:Number;
		
		/**
		 * Returns the speed of this instance
		 */
		public function get length():Number{
			return Math.sqrt(x*x + y*y);
		}
		/**
		 * Sets the speed of this instance
		 */
		public function set length(value:Number):void{
			if(length){
				scale(value / length);
			}
			else{
				x = value;
			}
		}
		/**
		 * Returns the angle of this instance in radians
		 */
		public function get angle():Number{
			return Math.atan2(x, y);
		}
		/**
		 * Sets the angle of this instance in radians
		 * 
		 * @param value An angle in radians
		 */
		public function set angle(value:Number):void{
			var r:Number = length;
			x = r * Math.cos(value);
			y = r * Math.sin(value);
		}
		/**
		 * Returns the angle of this instance in degrees
		 */
		public function get angleD():Number{
			return MathD.atan2(x, y);
		}
		/**
		 * Sets the angle of this instance in degrees
		 * 
		 * @param value An angle in degrees
		 */
		public function set angleD(value:Number):void{
			var r:Number = length;
			x = r * MathD.cos(value);
			y = r * MathD.sin(value);
		}
		
		public function Vector2D(x:Number = 0, y:Number = 0) {
			this.x = x;
			this.y = y;
		}
		
		public function clone():Vector2D{
			return new Vector2D(x,y);
		}
		
		public function convertToPoint():Point{
			return new Point(x,y);
		}
		public function convertFromPoint(point:Point):Vector2D{
			return new Vector2D(point.x,point.y);
		}
		
		public function equals(vector:Vector2D):Boolean{
			return vector.x == x && vector.y == y;
		}
		/**
		 * Adds vector to this instance
		 */
		public function plus(vector:Vector2D, clone:Boolean = false):Vector2D{
			if(clone){
				return plusClone(vector);
			}
			x += vector.x;
			y += vector.y;
			return null;
		}
		/**
		 * Returns a clone of this instance with vector added to it
		 */
		protected function plusClone(vector:Vector2D):Vector2D{
			return new Vector2D(x + vector.x, y + vector.y);
		}
		/**
		 * Subtracts vector from this instance
		 */
		public function minus(vector:Vector2D, clone:Boolean = false):Vector2D{
			if(clone){
				return minusClone(vector);
			}
			x -= vector.x;
			y -= vector.y;
			return null;
		}
		/**
		 * Returns a clone of this instance with vector subtracted from it
		 */
		protected function minusClone(vector:Vector2D):Vector2D{
			return new Vector2D(x -vector.x, y - vector.y);
		}
		/**
		 * Reverses this vector
		 * 
		 * This method uses scale(-1) internally
		 */
		public function negate(clone:Boolean = false):Vector2D{
			if(clone){
				return negateClone();
			}
			scale(-1);
			return null;
		}
		/**
		 * Returns a negated clone of this instance
		 */
		protected function negateClone():Vector2D{
			return new Vector2D(-x, -y);
		}
		/**
		 * Scales this instance with value
		 */
		public function scale(value:Number, clone:Boolean = false):Vector2D{
			if(clone){
				return scaleClone(value);
			}
			x *= value;
			y *= value;
			return null;
		}
		/**
		 * Returns a new scaled clone of this instance
		 */
		protected function scaleClone(value:Number):Vector2D{
			return new Vector2D(x * value, y * value);
		}
		/**
		 * Rotate instance with rad angle. In radians
		 */
		public function rotate(rad:Number, clone:Boolean = false):Vector2D{
			if(clone){
				return rotateClone(rad);
			}
			var cRad:Number = Math.cos(rad);
			var sRad:Number = Math.sin(rad);
			var rX:Number = x * cRad - y * sRad;
			var rY:Number = x * sRad - y * cRad;
			x = rX;
			y = rY;
			return null;
		}
		/**
		 * Rotate instance with deg angle. In degrees
		 */
		public function rotateD(deg:Number, clone:Boolean = false):Vector2D{
			if(clone){
				return rotateDClone(deg);
			}
			var cDeg:Number = MathD.cos(deg);
			var sDeg:Number = MathD.sin(deg);
			var rX:Number = x * cDeg - y * sDeg;
			var rY:Number = x * sDeg - y * cDeg;
			x = rX;
			y = rY;
			return null;
		}
		/**
		 * Returns a rotated clone of this instance with rad angle. In radians
		 */
		protected function rotateClone(rad:Number):Vector2D{
			var rt:Vector2D = clone();
			rt.rotate(rad);
			return rt;
		}
		/**
		 * Returns a rotated clone of this instance with deg angle. In degrees
		 */
		protected function rotateDClone(deg:Number):Vector2D{
			var rt:Vector2D = clone();
			rt.rotateD(deg);
			return rt;
		}
		/**
		 * Returns the Dot product of 2 vectors.
		 */
		public static function dot(vectorA:Vector2D, vectorB:Vector2D):Number{
			return (vectorA.x * vectorB.x) + (vectorA.y * vectorB.y);
		}
		/**
		 * Returns the normal of a vector (a new vector rotated 90 degrees)
		 */
		public static function normal(vector:Vector2D, inverse:Boolean = false):Vector2D{
			if(inverse){
				return new Vector2D(-vector.y, vector.x).negate(true);
			}
			else{
				return new Vector2D(-vector.y, vector.x);
			}
		}
		/**
		 * Tests if vector is perpendicular to this instance
		 */
		public static function perpendicular(vectorA:Vector2D, vectorB:Vector2D):Boolean{
			return Vector2D.dot(vectorA, vectorB) == 0;
		}
		/**
		 * Returns the angle between 2 vectors in radians
		 */
		public static function angleBetween(vectorA:Vector2D, vectorB:Vector2D):Number{
			var cosAngle:Number = Vector2D.dot(vectorA, vectorB) / (vectorA.length * vectorB.length);
			return Math.acos(cosAngle);
		}
		public static function angleDBetween(vectorA:Vector2D, vectorB:Vector2D):Number{
			var cosAngle:Number = Vector2D.dot(vectorA, vectorB) / (vectorA.length * vectorB.length);
			return MathD.acos(cosAngle);
		}
	}
}
